package org.study.jpa.boot.config;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.hibernate.autoconfigure.HibernateProperties;
import org.springframework.boot.hibernate.autoconfigure.HibernateSettings;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.context.annotation.*;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.Map;

/**
 * 可以看到内容跟使用 JdbcTemplate 时是一模一样的。通过 @ConfigurationProperties 可以知道这两个数据源
 * 分别加载了 spring.datasource.primary.* 和 spring.datasource.secondary.* 的配置。@Primary 注解
 * 指定了主数据源，就是当我们不特别指定哪个数据源的时候，就会使用这个 Bean 真正差异部分在下面的JPA配置上。
 * <p>
 * Primary 数据源的 JPA 配置。说明与注意：
 * 1、在使用 JPA 的时候，需要为不同的数据源创建不同的 package 来存放对应的 Entity 和 Repository，以便于配置类的分区扫描
 * 2、类名上的注解 @EnableJpaRepositories 中指定 Repository 的所在位置
 * 3、LocalContainerEntityManagerFactoryBean 创建的时候，指定 Entity 所在的位置
 * 4、其他主要注意在互相注入时候，不同数据源不同配置的命名，基本就没有什么大问题了
 *
 * @author Administrator
 * @date 2021-11-29
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactoryBean",
        transactionManagerRef = "primaryTransactionManager",
        basePackages = {"org.study.jpa.boot.repository.primary"}// 设置 Repository 所在位置
)
public class PrimaryConfig {
    @Autowired
    private JpaProperties jpaProperties;
    @Autowired
    private HibernateProperties hibernateProperties;

    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        DataSource dataSource = DataSourceBuilder.create().build();
        return dataSource;
    }

    @Primary
    @Bean(name = "entityManagerPrimary")
    public EntityManager primaryEntityManager(@Qualifier("primaryDataSource") DataSource primaryDataSource, EntityManagerFactoryBuilder builder) {
        LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = this.primaryEntityManagerFactoryBean(primaryDataSource, builder);
        EntityManagerFactory entityManagerFactory = localContainerEntityManagerFactoryBean.getObject();
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        return entityManager;
    }

    @Primary
    @Bean(name = "primaryEntityManagerFactoryBean")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactoryBean(@Qualifier("primaryDataSource") DataSource primaryDataSource, EntityManagerFactoryBuilder builder) {
        Map<String, Object> getVendorProperties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = builder.dataSource(primaryDataSource)
                // 设置实体类所在位置
                .packages("org.study.jpa.boot.entity.primary")
                .persistenceUnit("primaryPersistenceUnit")
                .properties(getVendorProperties)
                .build();
        return entityManagerFactoryBean;
    }

    @Primary
    @Bean(name = "primaryTransactionManager")
    public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource primaryDataSource, EntityManagerFactoryBuilder builder) {
        LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = this.primaryEntityManagerFactoryBean(primaryDataSource, builder);
        EntityManagerFactory entityManagerFactory = localContainerEntityManagerFactoryBean.getObject();
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(entityManagerFactory);
        return jpaTransactionManager;
    }
}
